/**
 * kl yy-ds @2021
 */
package com.cxps.server.alipay;

import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.cxps.cloud.common.ConstantPool;
import com.cxps.cloud.enums.ApiCode;
import com.cxps.cloud.message.ApiResponse;
import com.cxps.server.config.AlipayConfig;
import com.cxps.server.domain.BizContent;
import com.cxps.server.domain.PayOrder;
import com.cxps.server.domain.PayOrderDetails;
import com.cxps.server.domain.order.OrderDetails;
import com.cxps.server.domain.order.OrderDetailsForm;
import com.cxps.server.domain.order.PayOrderEntity;
import com.cxps.server.feign.FeignOrder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.apache.rocketmq.spring.support.RocketMQHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.*;

/**
 * 支付宝支付
 *
 * @author YouCai.Liu
 * @since 2021/11/17
 * ...
 */
@Slf4j
@RestController
@RequestMapping("/pay/alipay")
public class AlipayController {


    // 支付宝支付客户端
    @Autowired
    private AlipayClient alipayClient;

    // 远程调用订单服务
    @Resource
    private FeignOrder feignOrder;

    @Autowired
    private AlipayService alipayService;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 调用支付接口
     *
     * @return
     */
    @PostMapping("/do")
    public ApiResponse doPost(@RequestBody PayOrder payOrder) {

        try {
            Assert.notNull(payOrder, "支付订单信息不能为空");
        } catch (IllegalArgumentException e) {
            log.error("调用支付接口异常：" + e.getMessage(), e);
            return ApiResponse.fireError(e.getMessage());
        }

        // 从支付订单信息获取，会员ID，订单的详细信息
        String vipId = payOrder.getVipId();
        List<PayOrderDetails> orderDetails = payOrder.getList();

        // 商品总价/订单总价
        Double countPrice = 0.00;
        // 商品清单名
        StringBuilder productNameSb = new StringBuilder();
        // 订单清单
        List<OrderDetails> OrderDetails = new ArrayList<>();

        // 遍历支付订单里的详细清单
        for (PayOrderDetails aorder : orderDetails) {
            // 计算总价
            BigDecimal b1 = new BigDecimal(aorder.getProductNum());
            BigDecimal b2 = new BigDecimal(aorder.getProductPrice()).setScale(2, BigDecimal.ROUND_HALF_UP);
            BigDecimal multiply = b1.multiply(b2);
            double totalPrice = multiply.doubleValue();
            countPrice += totalPrice;

            // 商品清单
            productNameSb.append(aorder.getProductName()).append("、");

            // 构建订单清单信息
            OrderDetails orderDetails1 = new OrderDetails();
            orderDetails1.setProductId(aorder.getProductId());
            orderDetails1.setProductNum(aorder.getProductNum());
            orderDetails1.setProductPrice(aorder.getProductPrice());
            orderDetails1.setTotalPrice(totalPrice);
            OrderDetails.add(orderDetails1);
        }

        // 调用订单服务，生成订单
        OrderDetailsForm form = new OrderDetailsForm();
        form.setVipId(vipId);
        form.setOrderPrice(countPrice.toString());
        form.setRemark(".....");
        form.setOrderDetails(OrderDetails);
        ApiResponse apiResponse = feignOrder.orderSubmit(form);
        String code = apiResponse.getCode();
        if (!ApiCode.SUCCESS.getCode().equals(code)) {
            String errorMsg = "调用订单服务生成订单异常：" + apiResponse.getMsg();
            log.error(errorMsg);
            return ApiResponse.fireError(errorMsg);
        }
        String orderNum = apiResponse.getData().toString();

        // 生成支付表单
        try {
            String alipayForm = buildAlipayForm(orderNum, countPrice.toString(), productNameSb.toString());
            return ApiResponse.fireSuccess(alipayForm);
        } catch (AlipayApiException e) {
            log.error("生成支付表单异常：" + e.getMessage(), e);
            return ApiResponse.fireError(e.getMessage());
        }

    }

    /**
     * 生成支付宝支付的 form 表单
     *
     * @param orderNum   订单号
     * @param countPrice 商品总价
     * @param subject    支付主题
     * @return
     * @throws AlipayApiException
     */
    private String buildAlipayForm(String orderNum, String countPrice, String subject) throws AlipayApiException {
        // 创建支付API对应的request
        AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();

        // 在公共参数中设置回跳和通知地址
        alipayRequest.setReturnUrl(AlipayConfig.RETURN_URL);
        alipayRequest.setNotifyUrl(AlipayConfig.NOTIFY_URL);

        // 设置支付宝业务上下文
        BizContent bizContent = new BizContent(orderNum, countPrice, subject);
        String bizCon = JSON.toJSONString(bizContent);

        // 填充业务参数
        alipayRequest.setBizContent(bizCon);

        // 调用SDK生成表单
        return alipayClient.pageExecute(alipayRequest).getBody();

    }

    /**
     * 支付宝异步通知回跳地址
     *
     * @return
     */
    @PostMapping("/notify")
    public String notifyUrl(HttpServletRequest request) {

        log.info("支付宝的异步通知===》》》");

        try {
            // 支付宝异步通知回调的参数集合
            Map<String, String> params = new HashMap<>();

            // 从请求中获取所有的参数集合，得到一个MAP集合
            Map<String, String[]> requestParams = request.getParameterMap();

            // 遍历从请求中获取所有的参数集合 排除sign、sign_type
            for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
                String name = iter.next();
                String[] values = requestParams.get(name);
                String valueStr = "";
                for (int i = 0; i < values.length; i++) {
                    valueStr = (i == values.length - 1) ? valueStr + values[i]
                            : valueStr + values[i] + ",";
                }
                //乱码解决，这段代码在出现乱码时使用
//                valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
                params.put(name, valueStr);
            }

            // 调用SDK验证签名
            boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.CHARSET, AlipayConfig.SIGN_TYPE);
            if (signVerified) {
                // 签名验证通过，验证业务（没有一模一样的业务）
                /**
                 * 需要严格按照如下描述校验通知数据的正确性：
                 * 1. 商户需要验证该通知数据中的 out_trade_no 是否为商户系统中创建的订单号；
                 * 2. 判断 total_amount 是否确实为该订单的实际金额（即商户订单创建时的金额）；
                 * 3. 校验通知中的 seller_id（或者 seller_email) 是否为 out_trade_no 这笔单据的对应的操作方（有的时候，一个商户可能有多个 seller_id/seller_email）；
                 * 4. 验证 app_id 是否为该商户本身。
                 * 上述 1、2、3、4 有任何一个验证不通过，则表明本次通知是异常通知，务必忽略
                 */
                boolean b = checkBiz(params);
                if (!b) {
                    return "failure";
                }
                // 以上支付宝通知数据必要验证通过之后，修改业务自身的逻辑：修改订单状态、减库存、加积分.....
                String orderNum = params.get("out_trade_no");

                // 使用消息队列，对项目进行解耦
                sendMsg(orderNum);

                // 购物车删除下单商品
                HashOperations hashOperations = redisTemplate.opsForHash();

                // 通过订单号查询订单归属用户以及商品集合
                List<PayOrderEntity> payOrderEntities = alipayService.getPayOrderInfoByOrderNum(orderNum);
                for (PayOrderEntity payOrderEntity : payOrderEntities) {
                    // redis key
                    String key = ConstantPool.R_CART_INFO + payOrderEntity.getVipId();

                    // 先从redis查询出购物车所有的商品
                    Map<Object, Object> entries = hashOperations.entries(key);
                    for (Object hashKey : entries.keySet()) {
                        // 获取hashKey
                        String hashKeyStr = hashKey.toString();
                        // 如果包含则删除
                        if(hashKeyStr.contains(payOrderEntity.getProductId())){
                            hashOperations.delete(key, hashKey);
                            continue;
                        }
                    }
                }


                // 使用feign 调用订单服务，修改订单状态
//                feignOrder.updateOrderStatus(params.get("out_trade_no"), OrderStatus.OK_PAY);

                //  使用feign 调用库存服务，修改商品库存

                //  使用feign 调用会员服务，给会员新增积分

                return "success";
            } else {
                // 验证失败
                return "failure";
            }
        } catch (Exception e) {
            e.printStackTrace();
            return "failure";
        }
    }

    /**
     * 发送消息到消息队列
     *
     * @param orderNum
     */
    private void sendMsg(String orderNum) {
        // 发送目的地  TOPIC:TAG
        String destination = "pay_order_topic" + ":" + "OK";

        String msgJson = "{\"orderNum\":\""+orderNum+"\", \"payType\":\"2\"}";

        // 构建消息
        Message message =
                // 发送的消息体，消息体必须存在，也就是要往MQ发送的消息
                MessageBuilder.withPayload(msgJson)
                        // 消息key，可以使用业务主键
                        .setHeader(RocketMQHeaders.KEYS, orderNum)
                        .build();
        // 同步发送消息
        rocketMQTemplate.syncSend(destination, message, 60000);
    }

    /**
     * 验证支付宝回调业务参数
     *
     * @param params
     * @return
     */
    private boolean checkBiz(Map<String, String> params) {

        // 1。验证订单号
        String orderNum = params.get("out_trade_no");
        // 通过订单号查询订单金额；一能证明订单号是我们业务的。二可以验证订单的实际交易金额
        String orderPrice = alipayService.getOrderPrice(orderNum);
        if (StringUtils.isBlank(orderPrice)) {
            return false;
        }

        // 2。验证订单实际金额
        String totalAmount = params.get("total_amount");
        if (!orderPrice.equals(totalAmount)) {
            return false;
        }

        // 3。校验卖家身份
        String sellerId = params.get("seller_id");
        String mySellerId = AlipayConfig.SELLER_ID;
        if (!mySellerId.equals(sellerId)) {
            return false;
        }
        // 4。校验 app_id
        String appId = params.get("app_id");
        String myAppId = AlipayConfig.APP_ID;
        if (!myAppId.equals(appId)) {
            return false;
        }

        return true;
    }


    /**
     * 支付宝同步通知回跳地址
     *
     * @return
     */
    @GetMapping("/returnUrl")
    public void returnUrl(HttpServletResponse httpResponse) {
        /**
         * http://192.168.42.233/pay/returnUrl?
         * charset=UTF-8&
         * out_trade_no=20211109000001&
         * method=alipay.trade.page.pay.return&
         * total_amount=0.01&
         * sign=O9BACL69007WCJWP94NgwBPsUOauWX7NimUawl9YgP9m865SSWtZAQSkowUlFXdH6uTMIeBuGlTwusxMVPLM24LvwmoWIxwbBGJ1u11IpaX9AGgFkf7aWmktJ59wQwbJR8DAUexnuStkWoBo0/eXhoSZ2j3dws7Qk9nS5TpktS9aV1%2BD7gWjUlArSrGaB0qeeiDDBn8IM0HAjWH7v4jFoMBpHXkagtF2Fc3tcIxMqVT3UUAbBVYHt5JyKKPc9pirOzmbH7EEJFpTCwrx1EzRAibutHe1yYb8rvIkLZ335h6B/BQw0QA9svBJUCRqxrdgQ/8KzhFU6rUS2/VLmVTKw==&
         * trade_no=2021110922001473670501983537&
         * auth_app_id=2021000118648009&
         * version=1.0&
         * app_id=2021000118648009&
         * sign_type=RSA2&
         * seller_id=2088621957094122&
         * timestamp=2021-11-09+11%3A49%3A54
         */
        log.info("支付宝的同步通知===》》》");


        // 1、验证返回信息的准确性/正确性

        // 2、修改订单状态、减库存、积分

    }

    public static void main(String[] args) {
//        BigDecimal b2 = new BigDecimal(2.23747);
//        BigDecimal bigDecimal = b2.setScale(2, BigDecimal.ROUND_HALF_UP);
//        System.out.println(bigDecimal.doubleValue());

        int a = Integer.MAX_VALUE;
        int b = Integer.MIN_VALUE;
        System.out.println(a);
        System.out.println(b);
        if (a < b) {
            System.out.println("a < b");
        }
        if (a - b < 0) {
            System.out.println("a - b < 0");
        }

    }

}
